﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using MentalAlchemy.Atomics;

namespace MentalAlchemy.Compounds.Image
{
	/// <summary>
	/// [compound]
	/// 
	/// Base class for different regions of interest, which can be drawn in within user controls.
	/// </summary>
	public abstract class RegionOfInterest
	{
		protected bool isSelected;	// indicates whether ROI is selected or not.

		private Color selectColor = Color.Orange;
		private Color unselectColor = Color.Green;
		protected const int SELECTION_BOX_SIZE = 7;
		protected const int SELECTION_BOX_HALFSIZE = SELECTION_BOX_SIZE / 2;

		protected Molecules.Image.RegionOfInterest roi;

		#region - Properties. -
		/// <summary>
		/// Selection color.
		/// </summary>
		public Color SelectColor
		{
			get { return selectColor; }
			set { selectColor = value; }
		}

		/// <summary>
		/// Unselection color.
		/// </summary>
		public Color UnselectColor
		{
			get { return unselectColor; }
			set { unselectColor = value; }
		}

		/// <summary>
		/// Associated Roi.
		/// </summary>
		public Molecules.Image.RegionOfInterest Roi
		{
			get { return roi; }
		}
		/// <summary>
		/// List of forms, which are in some way associated with the given ROI data.
		/// </summary>
		public List<Form> Forms { get; set; }

		/// <summary>
		/// Pointer to the control containing given ROI.
		/// </summary>
		public UserControl ParentControl { get; set; }
		#endregion

		#region - Construction. -
		protected RegionOfInterest()
		{
			Forms = new List<Form>();
		}
		protected RegionOfInterest(Color selColor, Color unselColor)
		{
			selectColor = selColor;
			unselectColor = unselColor;
			Forms = new List<Form>();
		}
		protected RegionOfInterest(RegionOfInterest argRoi)
		{
			roi = argRoi.Roi.Clone();
			selectColor = argRoi.selectColor;
			unselectColor = argRoi.unselectColor;
			isSelected = argRoi.isSelected;
			Forms = new List<Form>();
		}
		#endregion

		#region - Abstract methods. -
		public abstract RegionOfInterest Clone();
		public abstract void Draw(Graphics g);
		public abstract void Draw(Graphics g, Point point);
		#endregion

		#region - Public methods. -
		public void Select() { isSelected = true; }
		public void Unselect() { isSelected = false; }
		#endregion
	}

	/// <summary>
	/// [compound]
	/// 
	/// Class for region of interest with arbitrary shape.
	/// </summary>
	public class FlexibleRoi : RegionOfInterest
	{
		private const int POINT_SIZE = 3;
		private const int START_POINT_RADIUS = 2;

		private PointF selectedPoint = PointF.Empty;

		#region - Constructor. -
		protected FlexibleRoi() { }
		public FlexibleRoi(FlexibleRoi fRoi) : base(fRoi) {}
		public FlexibleRoi(Molecules.Image.FlexibleRoi fRoi, Color selColor, Color unselColor) : base(selColor, unselColor)
		{
			roi = fRoi;
		}
		#endregion

		#region - Public methods -
		public override RegionOfInterest Clone(){return new FlexibleRoi(this);}

		public override void Draw(Graphics g)
		{
			var fRoi = (Molecules.Image.FlexibleRoi)roi;
			var points = fRoi.Points;
			if (points.Count == 0) { return; }

			//
			// draw ROI lines.
			//
			var penColor = isSelected ? SelectColor : UnselectColor;
			var pen = new Pen(penColor, 2.0f);

			//if (fRoi.ChangeNodesMode) { pen.Width = 1; }

			if (points.Count > 1) g.DrawLines(pen, points.ToArray());
			for (int i = 1; i < points.Count; ++i)
			{
				// draw point circle
				RectangleF rect = new Rectangle();
				rect.X = points[i].X - POINT_SIZE / 2.0f;
				rect.Y = points[i].Y - POINT_SIZE / 2.0f;
				rect.Width = POINT_SIZE;
				rect.Height = POINT_SIZE;
				g.DrawEllipse(pen, rect);
			}

			if (fRoi.IsClosed)
			{
				// draw line from the beginning to the end.
				g.DrawLine(pen, points[0], points[points.Count - 1]);
			}
			else
			{
				// mark the very first point.
				RectangleF rect = new Rectangle();
				rect.X = points[0].X - START_POINT_RADIUS;
				rect.Y = points[0].Y - START_POINT_RADIUS;
				rect.Width = START_POINT_RADIUS * 2;
				rect.Height = START_POINT_RADIUS * 2;
				g.DrawEllipse(new Pen(Color.DarkBlue), rect);
			}

			// find bounding rect coordinates.
			RectangleF bRect = StructMath.GetBoundingRectangle(points);

			//
			// Draw ROI name using the found bounding rect.
			const int fontSize = 12;
			string name = "ROI " + roi.Number;
			var font = new Font("Arial", fontSize, FontStyle.Bold);
			var strSize = g.MeasureString(name, font);
			var brush = new SolidBrush(UnselectColor);
			g.DrawString(name,
						font, brush,
						bRect.X + bRect.Width * 0.5f - strSize.Width * 0.5f,
						bRect.Y + bRect.Height * 0.5f - strSize.Height * 0.5f);

			if (!selectedPoint.IsEmpty && isSelected)
			{
				// draw selected point with inverted color.
				// draw point circle
				var rect = new Rectangle();
				rect.X = (int)(selectedPoint.X - POINT_SIZE);
				rect.Y = (int)(selectedPoint.Y - POINT_SIZE);
				rect.Width = POINT_SIZE * 2;
				rect.Height = POINT_SIZE * 2;
				pen.Color = Color.White;
				g.DrawEllipse(pen, rect);
			}
		}

		public override void Draw(Graphics g, Point point)
		{
			var fRoi = (Molecules.Image.FlexibleRoi) roi;
			var points = fRoi.Points;
			if (points.Count == 0) { return; }

			Draw(g);

			if (!fRoi.IsClosed)
			{
				// Draw "temporal" line.
				var penColor = isSelected ? SelectColor : UnselectColor;
				var pen = new Pen(penColor, 1.5f);
				pen.DashStyle = DashStyle.Dash;
				g.DrawLine(pen, points[points.Count - 1], point);
			}
		}
		#endregion
	}

	/// <summary>
	/// [compound]
	/// 
	/// Class for elliptical region of interest.
	/// </summary>
	public class CircleRoi : RegionOfInterest
	{
		#region - Construction and initialization -
		protected CircleRoi() { }
		public CircleRoi(CircleRoi cRoi) : base(cRoi) { }
		public CircleRoi(Molecules.Image.CircleRoi cRoi, Color selColor, Color unselColor)
			: base(selColor, unselColor)
		{
			roi = cRoi;
		}
		#endregion

		#region - Public methods -
		public override RegionOfInterest Clone() { return new CircleRoi(this); }

		public override void Draw(Graphics g)
		{
			var cRoi = (Molecules.Image.CircleRoi) roi;
			PointF corner1 = cRoi.Corner1, corner2 = cRoi.Corner2;
			if (corner1 == PointF.Empty || corner2 == PointF.Empty) { return; }

			//
			// draw ROI circle.
			//
			var penColor = isSelected ? SelectColor : UnselectColor;
			var pen = new Pen(penColor, 2.0f);
			RectangleF rect = new Rectangle();
			rect.X = corner1.X < corner2.X ? corner1.X : corner2.X;
			rect.Y = corner1.Y < corner2.Y ? corner1.Y : corner2.Y;
			rect.Width = Math.Abs(corner1.X - corner2.X);
			rect.Height = Math.Abs(corner1.Y - corner2.Y);
			g.DrawEllipse(pen, rect);

			//
			// draw ROI name.
			//
			const int fontSize = 12;
			string name = "ROI " + roi.Number;
			var font = new Font("Arial", fontSize, FontStyle.Bold);
			var strSize = g.MeasureString(name, font);
			g.DrawString(name,
						font, new SolidBrush(UnselectColor),
						rect.X + rect.Width * 0.5f - strSize.Width * 0.5f,
						rect.Y + rect.Height * 0.5f - strSize.Height * 0.5f);

			if (isSelected)
			{
				//
				// draw bounds.
				//
				pen.Width = 1;
				pen.DashStyle = DashStyle.Dot;
				g.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);

				//
				// draw small squares to resize the ellipse.
				//
				pen.DashStyle = DashStyle.Solid;
				// upper left corner.
				var smRect = new Rectangle((int)rect.X - SELECTION_BOX_HALFSIZE, (int)rect.Y - SELECTION_BOX_HALFSIZE,
													SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// upper right corner.
				smRect = new Rectangle((int)rect.Right - SELECTION_BOX_HALFSIZE, (int)rect.Y - SELECTION_BOX_HALFSIZE,
													SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// lower right corner.
				smRect = new Rectangle((int)rect.Right - SELECTION_BOX_HALFSIZE, (int)rect.Bottom - SELECTION_BOX_HALFSIZE,
													SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// lower left corner.
				smRect = new Rectangle((int)rect.X - SELECTION_BOX_HALFSIZE, (int)rect.Bottom - SELECTION_BOX_HALFSIZE,
													SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);

				// left-side bound.
				int halfW = (int)rect.Width / 2,
					halfH = (int)rect.Height / 2;
				smRect = new Rectangle((int)rect.X - SELECTION_BOX_HALFSIZE, (int)rect.Y + halfH - SELECTION_BOX_HALFSIZE,
										SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// right-side bound.
				smRect = new Rectangle((int)rect.Right - SELECTION_BOX_HALFSIZE, (int)rect.Y + halfH - SELECTION_BOX_HALFSIZE,
										SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// upper-side bound.
				smRect = new Rectangle((int)rect.X + halfW - SELECTION_BOX_HALFSIZE, (int)rect.Y - SELECTION_BOX_HALFSIZE,
										SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// lower-side bound.
				smRect = new Rectangle((int)rect.X + halfW - SELECTION_BOX_HALFSIZE, (int)rect.Bottom - SELECTION_BOX_HALFSIZE,
										SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);

			}
		}

		public override void Draw(Graphics g, Point point)
		{
			var cRoi = (Molecules.Image.CircleRoi) roi;
			cRoi.Corner2 = point;
			Draw(g);
		}
		#endregion
	}

	/// <summary>
	/// [compound]
	/// 
	/// Class for rectangular region of interest.
	/// </summary>
	public class RectRoi : RegionOfInterest
	{
		#region - Construction and initialization -
		protected RectRoi() { }
		public RectRoi(RectRoi rRoi) : base(rRoi) { }
		public RectRoi(Molecules.Image.RectRoi rRoi, Color selColor, Color unselColor)
			: base(selColor, unselColor)
		{
			roi = rRoi;
		}
		#endregion

		#region - Public methods -
		public override RegionOfInterest Clone() { return new RectRoi(this); }

		public override void Draw(Graphics g)
		{
			var rRoi = (Molecules.Image.RectRoi) roi;
			PointF corner1 = rRoi.Corner1, corner2 = rRoi.Corner2;
			if (corner1 == PointF.Empty && corner2 != PointF.Empty) { return; }

			var leftTop = corner1.X < corner2.X ? corner1 : corner2;
			leftTop.Y = corner1.Y < corner2.Y ? corner1.Y : corner2.Y;

			//
			// draw ROI rectangle.
			//
			var penColor = isSelected ? SelectColor : UnselectColor;
			var pen = new Pen(penColor, 2.0f);
			RectangleF rect = new Rectangle();
			rect.X = corner1.X < corner2.X ? corner1.X : corner2.X;
			rect.Y = corner1.Y < corner2.Y ? corner1.Y : corner2.Y;
			rect.Width = Math.Abs(corner1.X - corner2.X);
			rect.Height = Math.Abs(corner1.Y - corner2.Y);
			g.DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);

			//
			// draw ROI name.
			//
			var fontSize = 12;
			var name = "ROI " + roi.Number;
			var font = new Font("Arial", fontSize, FontStyle.Bold);
			var strSize = g.MeasureString(name, font);
			g.DrawString(name,
						font, new SolidBrush(UnselectColor),
						rect.X + rect.Width * 0.5f - strSize.Width * 0.5f,
						rect.Y + rect.Height * 0.5f - strSize.Height * 0.5f);

			if (isSelected)
			{
				//
				// draw small squares to resize the rectangle.
				//
				pen.Width = 1;
				pen.DashStyle = DashStyle.Solid;
				// upper left corner.
				var smRect = new Rectangle((int)rect.X - SELECTION_BOX_HALFSIZE, (int)rect.Y - SELECTION_BOX_HALFSIZE,
													SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// upper right corner.
				smRect = new Rectangle((int)rect.Right - SELECTION_BOX_HALFSIZE, (int)rect.Y - SELECTION_BOX_HALFSIZE,
													SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// lower right corner.
				smRect = new Rectangle((int)rect.Right - SELECTION_BOX_HALFSIZE, (int)rect.Bottom - SELECTION_BOX_HALFSIZE,
													SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// lower left corner.
				smRect = new Rectangle((int)rect.X - SELECTION_BOX_HALFSIZE, (int)rect.Bottom - SELECTION_BOX_HALFSIZE,
													SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);

				// left-side bound.
				int halfW = (int)rect.Width / 2,
					halfH = (int)rect.Height / 2;
				smRect = new Rectangle((int)rect.X - SELECTION_BOX_HALFSIZE, (int)rect.Y + halfH - SELECTION_BOX_HALFSIZE,
										SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// right-side bound.
				smRect = new Rectangle((int)rect.Right - SELECTION_BOX_HALFSIZE, (int)rect.Y + halfH - SELECTION_BOX_HALFSIZE,
										SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// upper-side bound.
				smRect = new Rectangle((int)rect.X + halfW - SELECTION_BOX_HALFSIZE, (int)rect.Y - SELECTION_BOX_HALFSIZE,
										SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
				// lower-side bound.
				smRect = new Rectangle((int)rect.X + halfW - SELECTION_BOX_HALFSIZE, (int)rect.Bottom - SELECTION_BOX_HALFSIZE,
										SELECTION_BOX_SIZE, SELECTION_BOX_SIZE);
				g.DrawRectangle(pen, smRect);
			}
		}

		public override void Draw(Graphics g, Point point)
		{
			var rRoi = (Molecules.Image.RectRoi)roi;
			rRoi.Corner2 = point;
			Draw(g);
		}
		#endregion
	}
}
